package com.doubao.common.health;

import io.micrometer.core.instrument.MeterRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;


/**
 * Redis健康检查器
 * 定期检查Redis连接状态并提供监控指标
 */
@Component
@Slf4j
public class RedisHealthIndicator implements HealthIndicator {

    private final RedisConnectionFactory redisConnectionFactory;
    private final AtomicBoolean redisHealthy = new AtomicBoolean(true);
    private final AtomicLong lastSuccessTimestamp = new AtomicLong(System.currentTimeMillis());
    private final AtomicLong failureCount = new AtomicLong(0);
    private final AtomicLong responseTimeMs = new AtomicLong(0);

    private MeterRegistry meterRegistry;

    public RedisHealthIndicator(RedisConnectionFactory redisConnectionFactory) {
        this.redisConnectionFactory = redisConnectionFactory;
    }

    @Autowired(required = false)
    public void setMeterRegistry(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        if (meterRegistry != null) {
            // 注册监控指标
            meterRegistry.gauge("redis.health", redisHealthy, value -> value.get() ? 1.0 : 0.0);
            meterRegistry.gauge("redis.failures", failureCount, AtomicLong::doubleValue);
            meterRegistry.gauge("redis.response_time_ms", responseTimeMs, AtomicLong::doubleValue);
        }
    }

    @Override
    public Health health() {
        if (redisHealthy.get()) {
            return Health.up()
                    .withDetail("responseTime", responseTimeMs.get() + "ms")
                    .withDetail("lastSuccess", Instant.ofEpochMilli(lastSuccessTimestamp.get()))
                    .withDetail("failureCount", failureCount.get())
                    .build();
        } else {
            return Health.down()
                    .withDetail("failureCount", failureCount.get())
                    .withDetail("lastSuccess", Instant.ofEpochMilli(lastSuccessTimestamp.get()))
                    .build();
        }
    }

    /**
     * 每30秒检查一次Redis连接状态
     */
    @Scheduled(fixedRate = 30000)
    public void checkRedisHealth() {
        RedisConnection connection = null;
        Instant start = Instant.now();

        try {
            connection = redisConnectionFactory.getConnection();
            String pong = new String(connection.ping());

            if ("PONG".equalsIgnoreCase(pong)) {
                Instant end = Instant.now();
                long pingTime = Duration.between(start, end).toMillis();
                responseTimeMs.set(pingTime);

                if (!redisHealthy.get()) {
                    log.info("Redis连接恢复正常，响应时间: {}ms", pingTime);
                }

                redisHealthy.set(true);
                lastSuccessTimestamp.set(System.currentTimeMillis());
            } else {
                handleFailure("Redis ping响应异常: " + pong);
            }
        } catch (Exception e) {
            handleFailure("Redis连接检查失败: " + e.getMessage());
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (Exception e) {
                    log.warn("关闭Redis连接异常", e);
                }
            }
        }
    }

    private void handleFailure(String errorMessage) {
        redisHealthy.set(false);
        failureCount.incrementAndGet();
        log.error(errorMessage + ", 累计失败次数: {}", failureCount.get());
    }

    /**
     * 获取Redis健康状态
     */
    public boolean isRedisHealthy() {
        return redisHealthy.get();
    }

    /**
     * 获取上次成功连接的时间戳
     */
    public long getLastSuccessTimestamp() {
        return lastSuccessTimestamp.get();
    }

    /**
     * 获取失败次数
     */
    public long getFailureCount() {
        return failureCount.get();
    }

    /**
     * 获取响应时间
     */
    public long getResponseTimeMs() {
        return responseTimeMs.get();
    }
}